package com.newegg.hackathon.interfaces.api;

import com.newegg.hackathon.arangodb.dao.ItemDao;
import com.newegg.hackathon.arangodb.dao.SpecDao;
import com.newegg.hackathon.arangodb.model.Brand;
import com.newegg.hackathon.arangodb.model.Category;
import com.newegg.hackathon.arangodb.model.Item;
import com.newegg.hackathon.arangodb.model.Spec;
import com.newegg.hackathon.interfaces.BaseAPI;
import com.newegg.hackathon.model.Edge;
import com.newegg.hackathon.model.Graph;
import com.newegg.hackathon.model.ItemDetail;
import com.newegg.hackathon.model.Node;
import com.newegg.hackathon.model.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/item")
public class ItemAPI extends BaseAPI {

    @Autowired
    private ItemDao itemDao;
    @Autowired
    private SpecDao specDao;

    @GetMapping("/detail")
    @ResponseBody
    public Response<Object> detail(@RequestParam(name = "item", required = false) String itemnumber){
    	Item item = null;
    	if(itemnumber == null || "".equals(itemnumber)) {
    		item = itemDao.getOne();
        }else {
        	item = itemDao.get(itemnumber);
        }
    	if(item == null) { return FAIL(404); }
        return SUCCESS(new ItemDetail(item));
    }
    
    @GetMapping("/graph")
    @ResponseBody
    public Response<Object> graph(
    		@RequestParam(name = "item", required = false) String itemnumber, 
    		@RequestParam(name = "limit", required = false, defaultValue = "10") Integer limit
    	) throws InterruptedException, ExecutionException{
    	final Item item;
    	if(itemnumber == null || "".equals(itemnumber)) {
    		item = itemDao.getOne();
        }else {
        	item = itemDao.get(itemnumber);
        }
    	CompletableFuture<List<Item>> categoryNear = CompletableFuture.supplyAsync((Supplier<List<Item>>) ()->{
			return itemDao.findCategoryNear(item.getArangoId(), item.getCategory_id(), limit);
    	});
    	CompletableFuture<List<Item>> brandNear = CompletableFuture.supplyAsync((Supplier<List<Item>>) ()->{
			return itemDao.findBrandNear(item.getArangoId(), item.getBrand_id(), limit);
    	});
    	CompletableFuture<List<Item>> nocategoryNear = CompletableFuture.supplyAsync((Supplier<List<Item>>) ()->{
			return itemDao.findNoCategoryNear(item.getArangoId(), item.getCategory_id(), limit);
    	});
    	CompletableFuture<List<Item>> nobrandNear = CompletableFuture.supplyAsync((Supplier<List<Item>>) ()->{
			return itemDao.findNoBrandNear(item.getArangoId(), item.getBrand_id(), limit);
    	});
    	
		Graph graph = new Graph();
		graph.addNode(new Node(item, "main").setSize(100));
		
		Category category = item.getCategory();
		graph.addNode(new Node(category).setSize(70));
		graph.addEdge(new Edge(item.getArangoId(), category.getArangoId()));
		
		Brand brand = item.getBrand();
		graph.addNode(new Node(brand).setSize(70));
		graph.addEdge(new Edge(item.getArangoId(), brand.getArangoId()));
		
		Category nocategory = item.getCategory();
		graph.addNode(new Node("nocategory", "no" + nocategory.getArangoId(), "no" + nocategory.getId(), "Not in " + nocategory.getLabel()).setSize(70));
		graph.addEdge(new Edge(item.getArangoId(), "no" + nocategory.getArangoId()));
		
		Brand nobrand = item.getBrand();
		graph.addNode(new Node("nobrand", "no" + nobrand.getArangoId(), "no" + nobrand.getId(), "Not in " + nobrand.getLabel()).setSize(70));
		graph.addEdge(new Edge(item.getArangoId(), "no" + nobrand.getArangoId()));
		
		for (Spec spec : item.getSpecs()) {
			graph.addNode(new Node(spec).setSize(30));
			graph.addEdge(new Edge(item.getArangoId(), spec.getArangoId(), spec.getProp()));
		}
		for (Item targetItem : categoryNear.get()) {
			graph.addNode(new Node(targetItem, "category").setSize(50));
			graph.addEdge(new Edge(targetItem.getArangoId(), category.getArangoId()));
		}
		for (Item targetItem : brandNear.get()) {
			graph.addNode(new Node(targetItem, "brand").setSize(50));
			graph.addEdge(new Edge(targetItem.getArangoId(), brand.getArangoId()));
		}
		for (Item targetItem : nocategoryNear.get()) {
			graph.addNode(new Node(targetItem, "nocategory").setSize(50));
			graph.addEdge(new Edge(targetItem.getArangoId(), "no" + nocategory.getArangoId()));
		}
		for (Item targetItem : nobrandNear.get()) {
			graph.addNode(new Node(targetItem, "nobrand").setSize(50));
			graph.addEdge(new Edge(targetItem.getArangoId(), "no" + nobrand.getArangoId()));
		}
    	return GRAPH(graph);
    }
    
    @PostMapping("/findByspecs")
    @ResponseBody
    public Response<Object> findByspecs(
    		@RequestBody List<String> specs, 
    		@RequestParam(name = "limit", required = false, defaultValue = "10") Integer limit
    	) throws InterruptedException, ExecutionException{
    	Graph graph = new Graph();
    	Iterable<Spec> main_specs = specDao.findAllById(specs);
    	List<Item> items = itemDao.findItemBySpecs(specs, limit);
    	for (Spec spec : main_specs) {
    		graph.addNode(new Node(spec, "main"));
    		for (Spec spec1 : main_specs) {
    			if(spec1.getId().equals(spec.getId())){ continue; }
				graph.addEdge(new Edge(spec.getArangoId(), spec1.getArangoId()));
			}
    		for (Item item : items) {
    			graph.addNode(new Node(item, "item"));
    			graph.addEdge(new Edge(spec.getArangoId(), item.getArangoId()));
    			List<Spec> node_spec = new ArrayList<>(item.getSpecs());
				for (int i = 0; i < node_spec.size(); i++) {
					if(i > 5){ break; }
					Spec s = node_spec.get(i);
					if(specs.indexOf(s.getArangoId()) != -1){ continue; }
					graph.addNode(new Node("spec", s.getArangoId() + item.getArangoId(), s.getId(), s.getLabel()));
					graph.addEdge(new Edge(item.getArangoId(), s.getArangoId() + item.getArangoId()));
				}
			}
		}
    	return GRAPH(graph);
    }
    
    @GetMapping("/find")
    @ResponseBody
    public Response<Object> find(
    		@RequestParam(name="keyword", required = false, defaultValue="") String keyword,
    		@RequestParam(name="limit", required = false, defaultValue="10") Integer limit){
        return SUCCESS(itemDao.findItems(keyword, limit));
    }
}
